#!/usr/bin/python
# Copyright 2009 Red Hat, Inc. and/or its affiliates.
#
# Licensed to you under the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version. See the files README and
# LICENSE_GPL_v2 which accompany this distribution.
#

import sys
from optparse import OptionParser
import os
from ConfigParser import ConfigParser

import basicVdsTest
import confUtils
from confUtils import Validate
from testRunner import TestRunner

DEFAULT_CONFIG_PATH = "~/.ruthrc"

USAGE = '''usage: %%prog [options] [conf1 [conf2 [...] ] ]
Loads the configuration from '%s', unless other 'config' files are specified
in command line. For more help read \"README.1st\".''' % (DEFAULT_CONFIG_PATH)

CONFIG_TEMPLATE = {
    "global" : {
        "verbose" : {"default" : 1, "validator" : Validate.int},
        "modules" : {"validator" : Validate.list},
    }
}

def parseArguments():
    """
    Prepares the options parser and parses the cmd line args.
    """
    usage = USAGE
    parser = OptionParser(usage=usage)

    #Prepare generation configuration option
    parser.add_option("-g", "--generate-configuration",
        action="store", dest="moduleToGenerate", type="string", metavar="MODULE", default=None,
        help="Instead of running the suite. Creates a sample configuration from MODULE)")

    #prepare quiet option
    parser.add_option("-q", "--quiet",
        action="store_true", dest="quiet", default=False,
        help="Should I bother you with unnecessary niceties. (Hello message and end quote).")

    #prepare verbose option
    parser.add_option("-v",
        action="count", dest="verbosity", default=0,
        help="Override configurations' verbose level.")

   #prepare filter option
    parser.add_option("-f", "--filter-tests",
        action="store", dest="filter", default="*", metavar="GLOB",
        help="Only tests that match this glob filter will run." + \
             "Using '^' in the beginning of the glob means: Match opposite of GLOB.")

    #prepare debug option
    parser.add_option("-d", "--debug",
        action="store_true", dest="debug", default=False,
        help="Should I print a lot of output in case of internal errors.")

    #parse args
    options, args = parser.parse_args()
    if len(args) == 0:
        args = [DEFAULT_CONFIG_PATH]

    return (options, args)

def generateSampleConfigFile(defaultModule, suite, targetFile):
    #Generate template
    template = basicVdsTest.generateTemplateFromSuite(suite)

    #Add default module
    if not "global" in template:
        template["global"] = {}
    globalSection = template["global"]

    if not "modules" in globalSection:
        globalSection["modules"] = {}

    template["global"]["modules"]["default"] = defaultModule

    #Write it all to disk
    confUtils.generateSampleConfigFile(template, targetFile)

def handleSampleConfigFileGeneration(moduleToGenerate, targetFile):
    """
    Takes care of sample config generation.

    :param moduleToGenerate: The name of the python module.
                             Should be the same as in and :keyword:`import` statement.
    :param targetFile: The path to where to sample config file will be generated.

    :returns: **0** if successful, **400** on import error or **500** on config generation error.
    """
    #Import module
    try:
        print "Importing module '%s'..." % (moduleToGenerate)
        suiteModule = __import__(moduleToGenerate)
    except Exception, ex:
        print "Could not import module '%s'. (%s)" % (moduleToGenerate, ex)
        return 400

    #Get suite and generate config file
    try:
        print "Generating sample config file at '%s'..." % (targetFile)
        generateSampleConfigFile(moduleToGenerate, suiteModule.suite(), targetFile)
    except Exception, ex:
        print "Could not generate sample config file from module '%s'. (%s: %s)" % (moduleToGenerate, ex.__class__.__name__, ex)
        return 500

    return 0

def _printHeader(header, marker="="):
    sep = marker * len(header)
    print sep
    print header
    print sep

def runBatch(confFile, options):
    """
    Run a batch test as stated in a config file.
    """
    # Try to load config file
    mycfg = {}
    batchcfg = {}
    output = sys.stdout
    try:
        output.write("Validating configuration file '%s'.\n" % (os.path.split(confFile)[1]))
        output.flush()
        confUtils.validateConfigFile(CONFIG_TEMPLATE, confFile)
        output.write("Loading RUTH configuration.\n")
        batchcfg = ConfigParser()
        batchcfg.read(confFile)
        mycfg = confUtils.conf2dict(CONFIG_TEMPLATE, batchcfg)
    except Exception, ex:
        raise Exception("Could not load config file '%s'. Bailing out from batch. (%s: %s)" % (confFile, ex.__class__.__name__, ex))

    #Get modules to test
    modules = mycfg["global"]["modules"]
    output.write("Running tests from modules: %s.\n" % (", ".join(modules)))
    output.flush()

    #test modules
    batch = {}
    for mod in modules[:]:
        #import module
        imported_module = __import__(mod)

        try:
            imported_module.validateConfig(batchcfg)
            batch[mod] = imported_module
            output.write("Module '%s' is READY\n" % (mod))
        except Exception, ex:
            output.write("Module '%s' is NOT READY (%s: %s)\n" % (mod, ex.__class__.__name__, ex))
            modules.remove(mod)
    #set configuration
    basicVdsTest.BasicVDSTest.setConfig(batchcfg)

    results = []
    #run tests
    for mod in batch:
        output.write("Exercising module '%s'\n" % mod)
        output.flush()
        suite = batch[mod].suite()
        verbose = mycfg["global"]["verbose"]

        if options.verbosity > 0:
            verbose = options.verbosity

        results.append(TestRunner(verbosity=verbose, stream=output, filter=options.filter).run(suite))

    return results

def main():
    hello = """
    Hello, nice to meet you. I am RUTH - "Regression and Unit Test Harness".
    I am going to run a comprehensive test suite in order to validate vdsm
    functionality. However, I may require some assistance from you in order
    to correctly bootstrap the whole procedure.
    Use --help to see what you can do with me.
    """

    options, args = parseArguments()

    if not options.quiet:
        print hello

    if options.moduleToGenerate:
        return handleSampleConfigFileGeneration(options.moduleToGenerate, args[0])

    #iterate config files and run their tests
    configFiles = args
    i = 0
    results = []
    isMultipleConfigMode = len(configFiles) > 1
    for confFile in configFiles:
        i += 1
        if isMultipleConfigMode:
            _printHeader("Processing batch %d of %d. Configuration is '%s'." % (i, len(configFiles), os.path.split(confFile)[1]))
        try:
            results.extend(runBatch(os.path.expanduser(confFile), options))
        except Exception, ex:
            if options.debug:
                import traceback
                print traceback.format_exc()
            print ex

    if isMultipleConfigMode:
        totalFailures = sum([len(result.failures) for result in results])
        totalErrors = sum([len(result.errors) for result in results])
        _printHeader("Totals: Failures %d, Errors %d." % (totalFailures, totalErrors))

    if not options.quiet:
        print 'All Done!\nremember:\n\t"To Err is Human, To Test is Divine!"'

if __name__ == '__main__':
    main()
